#include "./thread_pool/pool.h"
#include "media_lib.h"
#include "../proto/proto.h"

//为了将频道列表的数组作为任务函数的参数
typedef struct {
	struct mlib_st *mlib;
	int chn_nr;
}my_chn_list_t;

static int sock_init (void);
static void *task_send_chn_list (void * args);
static void *task_send_chn_data (void *args);

static int udp_socket;
static struct sockaddr_in groupaddr;//组播地址

int main (void)
{
	threadpool_t *mypool;
	my_chn_list_t mychn = {NULL, 0};

	//1.初始化套接字
	//socket 将套接字设置为组播地址 setsockopt(2)
	sock_init ();

	//2.初始化线程池
	mypool = threadpool_init (MAX_CHN_NR, 3, MAX_CHN_NR);

	//3. 获取频道列表 
	mlib_get_chn_list (&mychn.mlib, &mychn.chn_nr);
	printf ("[%d]debug...\n", __LINE__);

	for (int i = 0; i < mychn.chn_nr; i++) {
		printf ("%d %s\n", (mychn.mlib)[i].chnid, (mychn.mlib)[i].descr);
	}

	//4. 添加发送频道列表的任务
	threadpool_add_task (mypool, task_send_chn_list, &mychn);

	//5. 有多少个频道, 就需要并发多少个线程, 同时发送
	//对应频道的数据
	for (int i = 0; i < mychn.chn_nr; i++) {
		threadpool_add_task (mypool, task_send_chn_data, &((mychn.mlib)[i].chnid));
	}

	while (1) 
		pause();
	return 0;
}

//初始化套接字
static int sock_init (void)
{
	struct ip_mreqn imr;
	//创建套接字
	udp_socket = socket (AF_INET, SOCK_DGRAM, 0);
	if (-1 == udp_socket) {
		perror ("socket()");
		return -1;
	}
	//设置多播组
	inet_aton (GROUP_IP, &imr.imr_multiaddr);
	inet_aton ("0.0.0.0", &imr.imr_address);
	imr.imr_ifindex = if_nametoindex("ens33");
	if (-1 == setsockopt (udp_socket, IPPROTO_IP, IP_MULTICAST_IF, &imr, sizeof (imr)))
	{
		perror ("setsockopt()");
		goto ERROR;
	}
	//初始化目的地址
	groupaddr.sin_family = AF_INET;
	groupaddr.sin_port = htons(atoi(RCV_PORT));
	inet_aton (GROUP_IP, &groupaddr.sin_addr);

	return 0;
ERROR:
	close (udp_socket);
}

//发送频道列表
static void *task_send_chn_list (void * args)
{
	//整个频道列表的长度
	int all_len = 0;
	//每一个频道的长度
	int entry_len = 0;
	int i = 0;
	//周期性的向多播组中发送频道列表
	//proto.h中约定好的数据结构 
	struct chn_list_st *sndlist = NULL;
	struct chn_list_entry *entrylist = NULL;
	//chnlist 保存的频道列表
	my_chn_list_t *chnlist = args;
	printf ("[%d]debug...\n", __LINE__);

	//将每一个频道的结构组成一个包进行发送
	sndlist = malloc (MAX_SIZE);
	if (NULL == sndlist) {
		return NULL;
	}

	//频道列表的频道号
	sndlist->chnid = 0;
	all_len = sizeof (sndlist->chnid);
	
	for(i = 0; i < chnlist->chn_nr; i++) {	
		entry_len = sizeof (int8_t) + sizeof (int8_t) + strlen((chnlist->mlib)[i].descr) + 1;
		//entrylist保存每一个频道的结构
		entrylist = malloc (entry_len);
		entrylist->chnid = (chnlist->mlib)[i].chnid; //当前频道的频道号
		entrylist->len = entry_len; //当前频道的自述长度
		printf ("自述长度:%d\n", entrylist->len);
		strcpy (entrylist->descr, (chnlist->mlib)[i].descr); //当前频道描述文件中的信息
		//将单个频道的数据包copy到整个频道列表
		if (all_len + entry_len >= MAX_SIZE) {
			break;
		}
		printf ("all_len = %d\n", all_len);
		memcpy ((char *)sndlist + all_len, entrylist, entry_len);
		all_len += entry_len; 
		free (entrylist);
		entrylist = NULL;
	}

	while (1) {
		//将组织好的频道列表包发送到多播组中
		sendto (udp_socket, sndlist, all_len, 0, (struct sockaddr *)&groupaddr, sizeof(groupaddr));
		//注意：这儿得做流控！
		sleep (1);
	}
	return NULL;
}

/*发送频道数据*/
static void *task_send_chn_data (void *args)
{
	ssize_t cnt = 0;
	struct chn_data_st *snd_data_p = NULL;

	//args将来保存的是传进来的频道号
	int8_t my_chn_id = *((int8_t *)args);
	printf ("[%d]debug...\n", __LINE__);

	snd_data_p = malloc (MAX_SIZE);
	if (snd_data_p == NULL) {
		perror ("malloc()");
		return NULL;
	}
	snd_data_p->chnid = my_chn_id;
	
	while (1) {
		cnt = mlib_read_chn_data (my_chn_id, snd_data_p->msg, MAX_SIZE-sizeof(int8_t)); 
		if (-1 == cnt) {
			fprintf (stderr, "%d频道出错", my_chn_id);
			return NULL;
		}
		sendto (udp_socket, snd_data_p, cnt+sizeof(int8_t), 0, (struct sockaddr *)&groupaddr, sizeof(groupaddr));
		//流控
		//sleep(1);
	}
}






